package com.bestcem.xm.ticket.service.manager.fsm;

import com.bestcem.xm.ticket.entity.mongo.Ticket;
import com.bestcem.xm.ticket.entity.mongo.TicketFlow;
import com.bestcem.xm.ticket.entity.mongo.TicketTemplate;
import com.bestcem.xm.ticket.enums.TicketEventEnum;
import com.bestcem.xm.ticket.enums.TicketStatusEnum;
import com.bestcem.xm.ticket.enums.TicketTypeEnum;
import com.bestcem.xm.ticket.service.manager.role.TicketAuditor;
import com.bestcem.xm.ticket.service.manager.role.TicketFollower;
import lombok.Getter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;

import java.util.*;

/**
 * 工单系统
 * 工单系统最主要下面两个model
 * <p>
 * 1. 工单配置
 * 1. 工单基本配置
 * 2. 工单通知配置
 * 3. 工单生成规则 (决定是否生成工单)
 * 4. 工单流程    (决定工单后续走哪个流程, 也就是如何处理工单)
 * 2. 工单
 * 1. 工单主体 (记录)
 * 2. 工单记录 (工单状态变化和工单事件的历史记录)
 * <p>
 * 在生成工单的时候, 将工单所使用的配置和流程的具体信息记录在工单中, 之后工单的事件所依赖的就是生成时候的配置
 * 工单的状态转换和事件所涉及的业务逻辑 主要是三个:
 * 1. 发生事件后工单下一步进入哪个状态
 * 2. 记录对应的工单记录
 * 3. 触发的事件发送邮件通知
 * <p>
 * Usage: 使用是时候只要将 ticket 实例和 event 传入 TicketFSM的 transfer 函数中.
 * ticket = Ticket()
 * ticket_fsm = TicketFSM(ticket)
 * await ticket_fsm.transfer(TicketEvent.SYSTEM_CREATED)
 * <p>
 * 根据工单的状态和事件
 * 1. 决定转移到工单下一个状态
 * 2. 记录不同的工单记录
 * 3. 根据工单配置发送不同的通知
 *
 * @author XiaoYunSong <ys.xiao@idiaoyan.com>
 * @version v1.0
 * @date 2021/8/20 13:50
 */
@Slf4j
@ToString(callSuper = true)
@Getter
public final class TicketFsm {
    /**
     * 工单流转事件对应标题
     */
    private static final Map<TicketEventEnum, String> EVENT_TITLE_MAP = new HashMap<>();
    /**
     * 工单流转状态对应标题
     */
    private static final Map<TicketStatusEnum, String> STATUS_TITLE_MAP = new HashMap<>();

    static {
        EVENT_TITLE_MAP.put(TicketEventEnum.SYSTEM_CREATED, "创建工单");
        EVENT_TITLE_MAP.put(TicketEventEnum.FOLLOWING, "分配跟进");
        EVENT_TITLE_MAP.put(TicketEventEnum.FOLLOW_OVERDUE_REMIND, "跟进逾期提醒");
        EVENT_TITLE_MAP.put(TicketEventEnum.FOLLOW_OVERDUE, "跟进已逾期");
        EVENT_TITLE_MAP.put(TicketEventEnum.COMMIT_FOLLOWED, "提交跟进");
        EVENT_TITLE_MAP.put(TicketEventEnum.AUDITING, "分配审核");
        EVENT_TITLE_MAP.put(TicketEventEnum.COMMIT_AUDITED, "提交审核");
        EVENT_TITLE_MAP.put(TicketEventEnum.AUDIT_OVERDUE_REMIND, "审核逾期提醒");
        EVENT_TITLE_MAP.put(TicketEventEnum.AUDIT_OVERDUE, "审核逾期");
        EVENT_TITLE_MAP.put(TicketEventEnum.MANUAL_CLOSED, "人工关闭");
        EVENT_TITLE_MAP.put(TicketEventEnum.SYSTEM_CLOSED, "系统关闭");
        EVENT_TITLE_MAP.put(TicketEventEnum.TRANSFER_FOLLOW, "跟进转派");
        EVENT_TITLE_MAP.put(TicketEventEnum.FINISH, "工单处理完成");
        EVENT_TITLE_MAP.put(TicketEventEnum.FAILED, "工单处理失败");
    }

    static {
        STATUS_TITLE_MAP.put(TicketStatusEnum.CREATED, "创建中");
        STATUS_TITLE_MAP.put(TicketStatusEnum.FOLLOWING, "跟进中");
        STATUS_TITLE_MAP.put(TicketStatusEnum.AUDITING, "审核中");
        STATUS_TITLE_MAP.put(TicketStatusEnum.SYSTEM_CLOSED, "系统关闭");
        STATUS_TITLE_MAP.put(TicketStatusEnum.MANUAL_CLOSED, "手工关闭");
        STATUS_TITLE_MAP.put(TicketStatusEnum.CLOSED, "关闭");
        STATUS_TITLE_MAP.put(TicketStatusEnum.FINISH, "工单处理完成");
        STATUS_TITLE_MAP.put(TicketStatusEnum.FAILED, "工单处理失败");
        STATUS_TITLE_MAP.put(TicketStatusEnum.UNNEED_DEAL, "无需处理");
    }


    /**
     * 发生的事件
     */
    private Integer event;
    /**
     * 工单信息
     */
    private Ticket ticket;
    /**
     * 工单流程配置
     */
    private TicketFlow ticketFlow;
    /**
     * 工单模板配置
     */
    private TicketTemplate templateConfig;
    /**
     * 工单模板配置(默认同上)
     */
    private TicketTemplate followedTemplate;
    /**
     * 工单流程跟进配置
     */
    private List<TicketFlow.FollowConfig> followConfig;
    /**
     * 工单流程审核配置
     */
    private List<TicketFlow.FollowConfig> auditConfig;
    /**
     * 工单是否需要审核
     */
    private Boolean needAudit;
    /**
     * 第一跟进人
     */
    private TicketFollower firstFollower;
    /**
     * 第二跟进人
     */
    private TicketFollower secondFollower;
    /**
     * 第一审核人
     */
    private TicketAuditor firstAuditor;
    /**
     * 第二审核人
     */
    private TicketAuditor secondAuditor;
    /**
     * 工单类型
     */
    private TicketTypeEnum ticketType;


    private TicketFsm(Ticket ticket) {
        if (Objects.isNull(ticket)) {
            return;
        }
        if (log.isInfoEnabled()) {
            log.info("[Ticket] FSM INIT; Ticket:{}; Status:{}", ticket.getId(), ticket.getStatus());
        }

        this.event = null;
        this.ticket = ticket;
        this.ticketFlow = ticket.getTicketFlow();
        this.templateConfig = ticket.getTicketTemplate();
        this.followedTemplate = this.templateConfig;
        this.followConfig = Optional.ofNullable(this.ticketFlow).map(TicketFlow::getFollowConfig).orElse(new ArrayList<>());
        this.auditConfig = Optional.ofNullable(this.ticketFlow).map(TicketFlow::getAuditConfig).orElse(new ArrayList<>());
        this.ticketType = TicketTypeEnum.getByIndex(ticket.getType());

        // 初始化跟进人信息
        this.initFollower();
        // 初始化审核人信息
        this.initAuditor();
    }

    public static TicketFsm getInstance(Ticket ticket) {
        if (Objects.isNull(ticket)) {
            return null;
        }
        return new TicketFsm(ticket);
    }

    /**
     * 获取当前跟进人信息
     *
     * @return 当前工单跟进人信息
     * @author XiaoYunSong <ys.xiao@idiaoyan.com>
     * @date 2021/8/20 15:00
     */
    public TicketFollower currentFollower() {
        if (Integer.valueOf(1).equals(this.ticket.getCurrentFollowNode())) {
            return this.firstFollower;
        }
        if (Integer.valueOf(2).equals(this.ticket.getCurrentFollowNode())) {
            return this.secondFollower;
        }
        return null;
    }

    /**
     * 获取当前审核人信息
     *
     * @return 当前工单审核人信息
     * @author XiaoYunSong <ys.xiao@idiaoyan.com>
     * @date 2021/8/20 15:00
     */
    public TicketAuditor currentAuditor() {
        if (Integer.valueOf(1).equals(this.ticket.getCurrentAuditNode())) {
            return this.firstAuditor;
        }
        if (Integer.valueOf(2).equals(this.ticket.getCurrentAuditNode())) {
            return this.firstAuditor;
        }
        return null;
    }

    /**
     * 初始化跟进人信息
     *
     * @author XiaoYunSong <ys.xiao@idiaoyan.com>
     * @date 2021/8/20 14:54
     */
    private void initFollower() {
        int followCount = this.followConfig.size();
        if (log.isInfoEnabled()) {
            log.info("[Ticket] 工单 {} 跟进节点数量 {}", this.ticket.getId(), this.followConfig.size());
        }
        if (followCount > 0) {
            this.firstFollower = TicketFollower.getInstance(this.followConfig.get(0));
            if (Objects.nonNull(this.firstFollower) && log.isInfoEnabled()) {
                log.info("[Ticket] 工单 {} 第一跟进人 {}", this.ticket.getId(), this.firstFollower.getRoleId());
            }
        }
        if (followCount > 1) {
            this.secondFollower = TicketFollower.getInstance(this.followConfig.get(1));
            if (Objects.nonNull(this.secondFollower) && log.isInfoEnabled()) {
                log.info("[Ticket] 工单 {} 第二跟进人 {}", this.ticket.getId(), this.secondFollower.getRoleId());
            }
        }
    }

    /**
     * 初始化审核人信息
     *
     * @author XiaoYunSong <ys.xiao@idiaoyan.com>
     * @date 2021/8/20 14:54
     */
    private void initAuditor() {
        this.needAudit = Optional.ofNullable(this.ticketFlow).map(TicketFlow::getNeedAudit).orElse(false);
        if (log.isInfoEnabled()) {
            log.info("[Ticket] 工单 {} 是否需要审核 {}", this.ticket.getId(), this.needAudit);
        }
        int auditCount = this.auditConfig.size();
        if (log.isInfoEnabled()) {
            log.info("[Ticket] 工单 {}审核节点数量{}", this.ticket.getId(), auditCount);
        }
        if (auditCount > 0) {
            this.firstAuditor = TicketAuditor.getInstance(this.auditConfig.get(0));
            if (Objects.nonNull(this.firstAuditor) && log.isInfoEnabled()) {
                log.info("[Ticket] 工单 {}第一审核人{}", this.ticket.getId(), this.firstAuditor.getRoleId());
            }
        }
        if (auditCount > 1) {
            this.secondAuditor = TicketAuditor.getInstance(this.auditConfig.get(1));
            if (Objects.nonNull(this.secondAuditor) && log.isInfoEnabled()) {
                log.info("[Ticket] 工单 {}第二审核人{}", this.ticket.getId(), this.secondAuditor.getRoleId());
            }
        }
    }

    public void setEvent(Integer event) {
        this.event = event;
    }

    public void setFirstFollower(TicketFollower firstFollower) {
        this.firstFollower = firstFollower;
    }

    public void setSecondFollower(TicketFollower secondFollower) {
        this.secondFollower = secondFollower;
    }

    public void setTicketFlow(TicketFlow ticketFlow) {
        this.ticketFlow = ticketFlow;
    }
}
